#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libusb.h>
#include <string.h>
#include <pthread.h>

#include "hidapi.h"
#include "utils.h"
#include "adpcm.h"

#define AS_KEY_EVENT 1
#define AS_AUDIA_DATA 0

#define AS_KEY_DOWN 2
#define AS_KEY_UP 1

as_hid_dev_p asDev = NULL;

int isTesting = 0;
int isKeyTest = 0;
int isDataTest = 0;

int needDecode = 1;//use adpcm arithmetic decode audio data
adpcm_state adstate;
int frameDiffNum = -1;//流水号校验

FILE* audioFile;

void read_sn(unsigned char* buf)
{
    int ret;
    buf[0] = 0x00;
    buf[1] = 0x02;

    ret = set_control_transfer(asDev->dev_handle,asDev->bintf_num,buf,64);
    if(ret < 0){
	printf("unable to send report : %d\n", ret);
	return;
    }

    /* sleep(1); */

    ret = get_control_transfer(asDev->dev_handle,asDev->bintf_num,buf,64);

    if(ret < 0){
	printf("unable to get report : %d\n", ret);
	return;
    }
    printf("control transfer ok,sn length = %d\n",buf[1]);
}

int create_audio_file()
{
    char* fname = (char*)calloc(60,sizeof(char));
    char* ts = (char*)calloc(10,sizeof(char));
    get_time(ts);
    snprintf(fname,60,"%s_%s.pcm",needDecode?"decode":"orig",ts);
    printf("start write buffer, fname = %s \r\n",fname);

    if((audioFile = fopen(fname,"w")) == NULL){
	printf("can not create %s file \n", fname);
	free(fname);
	free(ts);
	return 0;	    
    }	

    free(fname);
    free(ts);
    return 1;
}

void process_hid_data(unsigned char* buf,int size)
{
    switch(buf[0]){
	case AS_AUDIA_DATA:
	    if(isDataTest && (audioFile != NULL)){
		printf("write audio data \n");
		if(needDecode){
		    //decode buffer before
		    //     byte0: type = 0x0;
		    //     byte1: packet index, 流水号,0~ff累加
		    //     byte2: packet length, 不包含文件头长度
		    //     byte3: 解压缩参数index
		    //     byte4: 解压缩参数wPresampleH, byte5: wPresampleL
		    //     byte6~byte63: encode data, 有多少填多少
		    if(buf[2] <= 5){
			printf("buf length is too short:%d\n",buf[2]);
			frameDiffNum = -1;
			return;
		    }
		    int ck = buf[1] - frameDiffNum;
		    if(ck == 1 || ck == -255){
			frameDiffNum = buf[1];
		    }else{
			printf("check flow number err, ck = %d\n",ck); 
			printf("check flow number err:frameDiffNum = %d\n", frameDiffNum);
			printf("check flow number err:buf[1] = %d\n", buf[1]);
			break;
		    }

		    adstate.index = buf[3];
		    adstate.valprev = buf[4] << 8 | buf[5];

		    int data_len = buf[2] * 4;
		    unsigned char dec_buf[data_len];
		    memset(dec_buf,0,data_len);
		    unsigned char* inp_buf = buf + 6;
		    adpcm_decoder((char*)inp_buf,(short*)dec_buf,data_len/2,&adstate);
		    
		    fwrite(dec_buf,sizeof(char),data_len,audioFile);
		}else{
		    //save original audio data
		    fwrite(buf,sizeof(char),size,audioFile);
		}
	    }
	    break;
	case AS_KEY_EVENT:
	    {
		if(isKeyTest){
		    if(buf[1] == AS_KEY_UP){
			printf("get key event up\n"); 
		    }else if(buf[1] == AS_KEY_DOWN){
			printf("get key event down\n"); 
		    }
		}    

		frameDiffNum = -1;
		break;
	    }
	default:break; 
    }
}

void read_hid_data()
{
    printf("start read hid data thread ...\n");
    unsigned char* buf;
    int ret,size;
    const size_t max_len = asDev->input_ep_max_packet_size;
    printf("max len = %Zu \n", max_len);

    //set up transfer object
    buf = malloc(max_len);
    asDev->transfer = libusb_alloc_transfer(0);

    while(isKeyTest || isDataTest){
	ret = libusb_interrupt_transfer(asDev->dev_handle,
		asDev->input_endpoint,
		buf,
		max_len,
		&size,
		0//no time out
		); 
	if(ret == 0){
	    process_hid_data(buf,size);
	}else{
	    printf("read hid data err %d\n", ret);
	    isKeyTest = 0;
	    isDataTest = 0;
	    break;
	}
    }
}

void start_test()
{
    printf("-------------start test-----------\n");

    isTesting = 1;

    while (isTesting)
    {
	int snlen;
	char x;
	unsigned char buf[64];

	printf("\n");
	printf(".----------test menu----------.\n");
	printf("|  r:read serial number       |\n");
	printf("|  k:voice key event test     |\n");
	printf("|  f:save voice data to file  |\n");
	printf("|  q:quit test                |\n");
	printf(".-----------------------------.\n");
	printf("\n");

	printf("please input r,k,f,... or q to operation test: ");
	scanf("%c",&x);
	while (getchar() != '\n');
	switch(x)
	{
	    case 'q':
	    case 'Q':
		if(isKeyTest){
		    printf("stop key test \n");
		    isKeyTest = 0;
		}else if(isDataTest){
		    printf("stop audio data test \n");
		    isDataTest = 0;
		    if(audioFile){
			fclose(audioFile);
		    }
		}else{
		    isTesting = 0;
		}
		break;
	    case 'r': 
	    case 'R': 
		printf("read serial number test:---------------\n"); 

		memset(buf,0,sizeof(buf));
		read_sn(buf);
		snlen = buf[1];
		if(snlen <= 0){
		    printf("get sn length is %d !\n", snlen);
		}else{
		    char sn[snlen*2];	
		    char h,l;
		    memset(sn,0,sizeof(sn));

		    for (int i = 0;i < snlen; ++i) {
			h = buf[i+2] & 0x0f;
			l = buf[i+2]>>4 & 0x0f;
			sprintf(sn+i*2,"%x%x",l,h);
		    }
		    printf("read sn result:%s\n",sn);
		}
		break;  
	    case 'k': 
	    case 'K': 
		printf("voice key event test:---------------\n"); 
		if(isKeyTest || isDataTest){
		    printf("test thread is already start\n");
		}else{
		    pthread_t read_key_thread;
		    pthread_create(&read_key_thread,NULL,(void*)read_hid_data,NULL);
		    pthread_detach(read_key_thread);
		}
		isKeyTest = 1;
		break;
	    case 'f': 
	    case 'F': 
		printf("save voice data to file:---------------\n"); 
		if(isKeyTest || isDataTest){
		    printf("test thread is already start\n");
		    if(!isDataTest){
			create_audio_file();
			frameDiffNum = -1;
		    }
		}else{
		    create_audio_file();
		    frameDiffNum = -1;

		    pthread_t read_data_thread;
		    pthread_create(&read_data_thread,NULL,(void*)read_hid_data,NULL);
		    pthread_detach(read_data_thread);
		}
		isDataTest = 1;
		break; 
	    default:  
		printf("input value not support !\n");
		break;
	}
    }

    printf("-------------end test-----------\n");
}


int main(int argc, char *argv[])
{
    if(argc != 3){
	printf(" argc err -------------------- \n");
	printf(" example: -------------------- \n");
	printf(" sudo ./hidtest vid pid\r\n");
	printf(" -----------------------------\n");
	return 1;
    }

    asDev = init_as_standard_interface(atoi(argv[1]), atoi(argv[2]));
    if(!asDev){
	printf("can not init aispeech standard interface device\n");
	goto _failed;
    }

    print_dev(asDev->usb_dev);
    //start test 
    start_test();    

    printf(" -----------------------------\n");

    destroy_as_standard_interface(asDev);

    printf("exit test !\n");

    return 0;
_failed:
    destroy_as_standard_interface(asDev);
    return -10;
}
